/*
 * Copyright (c) 2009-2011, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef ALKES_DEBUG_H_INCLUDED_
#define ALKES_DEBUG_H_INCLUDED_

#include "alkes/internal.h"
#include <stdarg.h>

#if !defined(__GNUC__)
#define AL_EMERG(fmt, ...)  _alkes_dprintf(EMERGENT, fmt, __VA_ARGS__)
#define AL_ALERT(fmt, ...)  _alkes_dprintf(ALERT, fmt, __VA_ARGS__)
#define AL_CRIT(fmt, ...)   _alkes_dprintf(CRITICAL, fmt, __VA_ARGS__)
#define AL_ERROR(fmt, ...)  _alkes_dprintf(ERROR, fmt, __VA_ARGS__)
#define AL_WARN(fmt, ...)   _alkes_dprintf(WARN, fmt, __VA_ARGS__)
#define AL_NOTICE(fmt, ...) _alkes_dprintf(NOTICE, fmt, __VA_ARGS__)
#define AL_INFO(fmt, ...)   _alkes_dprintf(INFO, fmt, __VA_ARGS__)
#define AL_NO_SUPPORT(fmt, ...) _alkes_dprintf(ERROR, fmt " IS NOT SUPPORTED!", __VA_ARGS__)
#else
#define AL_EMERG(fmt, ...)  _alkes_dprintf(EMERGENT, fmt, ## __VA_ARGS__)
#define AL_ALERT(fmt, ...)  _alkes_dprintf(ALERT, fmt, ## __VA_ARGS__)
#define AL_CRIT(fmt, ...)   _alkes_dprintf(CRITICAL, fmt, ## __VA_ARGS__)
#define AL_ERROR(fmt, ...)  _alkes_dprintf(ERROR, fmt, ## __VA_ARGS__)
#define AL_WARN(fmt, ...)   _alkes_dprintf(WARN, fmt, ## __VA_ARGS__)
#define AL_NOTICE(fmt, ...) _alkes_dprintf(NOTICE, fmt, ## __VA_ARGS__)
#define AL_INFO(fmt, ...)   _alkes_dprintf(INFO, fmt, ## __VA_ARGS__)
#define AL_NO_SUPPORT(fmt, ...) _alkes_dprintf(ERROR, fmt " IS NOT SUPPORTED!", ## __VA_ARGS__)
#endif


#if defined(ALKES_DEBUG)

#define AL_TRACE()          _alkes_dprintf(DEBUG, "%s(%d)", __FILE__, __LINE__)
#define AL_TRACE_API(exp)   do{exp;_alkes_dprintf(LOG, #exp);}while(0)
#if !defined(__GNUC__)
#define AL_DBG(fmt, ...)    _alkes_dprintf(DEBUG, fmt, __VA_ARGS__)
#else
#define AL_DBG(fmt, ...)    _alkes_dprintf(DEBUG, fmt, ## __VA_ARGS__)
#endif
#define AL_ASSERT(exp)      AL_VERIFY(exp)
#define AL_ASSERT_BREAK()   alkes_debug_break()
#define AL_PRE_COND_BREAK() alkes_debug_break()

#else

#define AL_TRACE()          do{}while(0)
#define AL_TRACE_API(exp)   exp
#define AL_DBG(fmt, ...)    do{}while(0)
#define AL_ASSERT(exp)      do{}while(0)
#define AL_ASSERT_BREAK()   alkes_abort()
#define AL_PRE_COND_BREAK() do{}while(0)

#endif

#define AL_VERIFY(exp)          \
    do {                        \
        if (!(exp)) {           \
            alkes_dprintf(AL_ERRLV_CRITICAL, "%s(%d): assert \"" #exp "\"\n", __FILE__, __LINE__); \
            AL_ASSERT_BREAK();  \
        }                       \
    } while(0)

#define AL_PRE_COND(exp, retval)                            \
    do {                                                    \
        if (!(exp)) {                                       \
            AL_ERROR("%s:(%d) pre-cond= " #exp, __FUNC_NAME__, __LINE__);  \
            AL_PRE_COND_BREAK();                            \
            return retval;                                  \
        }                                                   \
    } while (0)

typedef int (*alkes_error_callback)(const char *format, va_list arg);

AL_EXPORT_API void alkes_dprintf(uint32_t level, const char* fmt, ...);
AL_EXPORT_API uint32_t alkes_set_error_level(uint32_t new_level);
AL_EXPORT_API uint32_t alkes_get_error_level();
AL_EXPORT_API alkes_error_callback alkes_set_error_callback(alkes_error_callback new_callback);
AL_EXPORT_API alkes_error_callback alkes_get_error_callback();


#if !defined(__GNUC__)
#define _alkes_dprintf(level, fmt, ...)     \
    do{                                     \
        uint64_t _ct = alkes_get_usec();    \
        alkes_dprintf(AL_ERRLV_##level, "[%6d.%06d][" #level "]: " fmt "\n", (uint32_t)(_ct/(1000000)), (uint32_t)(_ct%1000000), __VA_ARGS__);\
    }while(0)
#else
#define _alkes_dprintf(level, fmt, ...)     \
    do{                                     \
        uint64_t _ct = alkes_get_usec();    \
        alkes_dprintf(AL_ERRLV_##level, "[%6d.%06d][" #level "]: " fmt "\n", (uint32_t)(_ct/(1000000)), (uint32_t)(_ct%1000000), ## __VA_ARGS__);\
    }while(0)
#endif

#define AL_ERRLV_EMERGENT   ((uint32_t)0x00000001)
#define AL_ERRLV_ALERT      ((uint32_t)0x00000002)
#define AL_ERRLV_CRITICAL   ((uint32_t)0x00000004)
#define AL_ERRLV_ERROR      ((uint32_t)0x00000008)
#define AL_ERRLV_WARN       ((uint32_t)0x00000100)
#define AL_ERRLV_NOTICE     ((uint32_t)0x00000200)
#define AL_ERRLV_INFO       ((uint32_t)0x00000400)
#define AL_ERRLV_DEBUG      ((uint32_t)0x00000800)

#define AL_ERRLV_ALL        ((uint32_t)0xffffffff)

#endif
